#!/usr/bin/env python3
"""
RedFire API Gateway集成测试
测试跨服务调用和端到端业务流程
"""

import asyncio
import aiohttp
import json
import time
import random
from typing import Dict, List, Any, Optional
from dataclasses import dataclass
from pathlib import Path

import structlog


# 配置日志
structlog.configure(
    processors=[
        structlog.stdlib.filter_by_level,
        structlog.stdlib.add_logger_name,
        structlog.stdlib.add_log_level,
        structlog.stdlib.PositionalArgumentsFormatter(),
        structlog.processors.TimeStamper(fmt="iso"),
        structlog.processors.StackInfoRenderer(),
        structlog.processors.format_exc_info,
        structlog.processors.UnicodeDecoder(),
        structlog.processors.JSONRenderer()
    ],
    context_class=dict,
    logger_factory=structlog.stdlib.LoggerFactory(),
    wrapper_class=structlog.stdlib.BoundLogger,
    cache_logger_on_first_use=True,
)

logger = structlog.get_logger(__name__)


@dataclass
class TestResult:
    """测试结果"""
    test_name: str
    success: bool
    response_time: float
    status_code: Optional[int] = None
    error_message: Optional[str] = None
    response_data: Optional[Dict] = None


class GatewayIntegrationTester:
    """网关集成测试器"""
    
    def __init__(self, gateway_url: str = "http://localhost:8000"):
        self.gateway_url = gateway_url.rstrip('/')
        self.session: Optional[aiohttp.ClientSession] = None
        self.results: List[TestResult] = []
    
    async def __aenter__(self):
        self.session = aiohttp.ClientSession()
        return self
    
    async def __aexit__(self, exc_type, exc_val, exc_tb):
        if self.session:
            await self.session.close()
    
    async def run_all_tests(self):
        """运行所有测试"""
        logger.info("🚀 Starting RedFire Gateway Integration Tests")
        
        test_suites = [
            ("Gateway Health Tests", self._test_gateway_health),
            ("Basic Routing Tests", self._test_basic_routing),
            ("Load Balancing Tests", self._test_load_balancing),
            ("Rate Limiting Tests", self._test_rate_limiting),
            ("Circuit Breaker Tests", self._test_circuit_breaker),
            ("Authentication Tests", self._test_authentication),
            ("Cross-Service Tests", self._test_cross_service_calls),
            ("Error Handling Tests", self._test_error_handling),
            ("Performance Tests", self._test_performance)
        ]
        
        for suite_name, test_func in test_suites:
            logger.info(f"📋 Running {suite_name}")
            try:
                await test_func()
                logger.info(f"✅ {suite_name} completed")
            except Exception as e:
                logger.error(f"❌ {suite_name} failed: {e}", exc_info=True)
        
        await self._generate_test_report()
    
    async def _test_gateway_health(self):
        """测试网关健康检查"""
        # 基础健康检查
        await self._make_request(
            "Gateway Health Check",
            "GET",
            "/gateway/health"
        )
        
        # 网关状态
        await self._make_request(
            "Gateway Status",
            "GET", 
            "/gateway/status"
        )
        
        # 路由配置
        await self._make_request(
            "Gateway Routes",
            "GET",
            "/gateway/routes"
        )
        
        # 服务实例
        await self._make_request(
            "Gateway Services",
            "GET",
            "/gateway/services"
        )
    
    async def _test_basic_routing(self):
        """测试基础路由功能"""
        test_routes = [
            ("/api/v1/users", "GET"),
            ("/api/v1/auth/login", "POST"),
            ("/api/v1/market/quotes", "GET"),
            ("/api/v1/notifications", "GET")
        ]
        
        for path, method in test_routes:
            test_name = f"Route {method} {path}"
            
            # 准备请求数据
            data = None
            if method == "POST":
                data = {"test": "data"}
            
            await self._make_request(test_name, method, path, data=data)
    
    async def _test_load_balancing(self):
        """测试负载均衡"""
        # 发送多个请求到同一服务，验证负载均衡
        path = "/api/v1/users"
        
        responses = []
        for i in range(10):
            result = await self._make_request(
                f"Load Balance Test {i+1}",
                "GET",
                path
            )
            responses.append(result)
        
        # 分析响应，检查是否分发到不同实例
        target_instances = set()
        for result in responses:
            if result.response_data and 'headers' in result.response_data:
                instance = result.response_data['headers'].get('X-Target-Instance')
                if instance:
                    target_instances.add(instance)
        
        logger.info(f"Load balancing distributed requests to {len(target_instances)} instances")
    
    async def _test_rate_limiting(self):
        """测试限流功能"""
        path = "/api/v1/auth/login"
        
        # 快速发送多个请求触发限流
        tasks = []
        for i in range(60):  # 超过限制的请求数
            tasks.append(self._make_request(
                f"Rate Limit Test {i+1}",
                "POST",
                path,
                data={"username": f"test{i}", "password": "test"}
            ))
        
        results = await asyncio.gather(*tasks, return_exceptions=True)
        
        # 统计限流响应
        rate_limited = sum(1 for r in results if isinstance(r, TestResult) and r.status_code == 429)
        logger.info(f"Rate limiting triggered for {rate_limited} requests")
    
    async def _test_circuit_breaker(self):
        """测试熔断器功能"""
        # 这里需要模拟服务故障来触发熔断器
        # 可以通过发送请求到不存在的服务或故意失败的端点
        path = "/api/v1/nonexistent/service"
        
        # 发送多个失败请求触发熔断器
        for i in range(10):
            await self._make_request(
                f"Circuit Breaker Test {i+1}",
                "GET",
                path
            )
            await asyncio.sleep(0.1)
    
    async def _test_authentication(self):
        """测试认证功能"""
        # 测试无认证访问受保护资源
        await self._make_request(
            "No Auth Access",
            "GET",
            "/api/v1/users/profile"
        )
        
        # 测试错误的认证信息
        headers = {"Authorization": "Bearer invalid-token"}
        await self._make_request(
            "Invalid Auth",
            "GET",
            "/api/v1/users/profile",
            headers=headers
        )
        
        # 测试正确的认证信息（如果有的话）
        headers = {"Authorization": "Bearer valid-token-here"}
        await self._make_request(
            "Valid Auth",
            "GET",
            "/api/v1/users/profile",
            headers=headers
        )
    
    async def _test_cross_service_calls(self):
        """测试跨服务调用"""
        # 模拟用户登录 -> 获取用户信息 -> 获取市场数据 -> 发送通知的流程
        
        # 1. 用户登录
        login_result = await self._make_request(
            "User Login",
            "POST",
            "/api/v1/auth/login",
            data={"username": "testuser", "password": "testpass"}
        )
        
        # 2. 获取用户信息
        user_result = await self._make_request(
            "Get User Info",
            "GET",
            "/api/v1/users/profile",
            headers={"Authorization": "Bearer test-token"}
        )
        
        # 3. 获取市场数据
        market_result = await self._make_request(
            "Get Market Data",
            "GET",
            "/api/v1/market/quotes?symbol=AAPL"
        )
        
        # 4. 发送通知
        notification_result = await self._make_request(
            "Send Notification",
            "POST",
            "/api/v1/notifications",
            data={
                "user_id": "test-user",
                "message": "Market data updated",
                "type": "info"
            }
        )
        
        logger.info("Cross-service call chain completed")
    
    async def _test_error_handling(self):
        """测试错误处理"""
        error_scenarios = [
            ("Invalid Path", "GET", "/api/v1/invalid/path"),
            ("Method Not Allowed", "DELETE", "/api/v1/auth/login"),
            ("Large Payload", "POST", "/api/v1/users", {"data": "x" * 10000}),
            ("Invalid JSON", "POST", "/api/v1/users", "invalid-json"),
        ]
        
        for test_name, method, path, *args in error_scenarios:
            data = args[0] if args else None
            await self._make_request(test_name, method, path, data=data)
    
    async def _test_performance(self):
        """测试性能"""
        # 并发请求测试
        concurrent_requests = 50
        path = "/api/v1/market/quotes"
        
        start_time = time.time()
        
        tasks = [
            self._make_request(f"Concurrent Test {i+1}", "GET", path)
            for i in range(concurrent_requests)
        ]
        
        results = await asyncio.gather(*tasks, return_exceptions=True)
        
        end_time = time.time()
        total_time = end_time - start_time
        
        successful_requests = sum(
            1 for r in results 
            if isinstance(r, TestResult) and r.success
        )
        
        avg_response_time = sum(
            r.response_time for r in results 
            if isinstance(r, TestResult)
        ) / len(results)
        
        requests_per_second = concurrent_requests / total_time
        
        logger.info(
            f"Performance test completed",
            concurrent_requests=concurrent_requests,
            successful_requests=successful_requests,
            total_time=total_time,
            avg_response_time=avg_response_time,
            requests_per_second=requests_per_second
        )
    
    async def _make_request(
        self,
        test_name: str,
        method: str,
        path: str,
        data: Any = None,
        headers: Optional[Dict[str, str]] = None
    ) -> TestResult:
        """发送HTTP请求"""
        url = f"{self.gateway_url}{path}"
        start_time = time.time()
        
        try:
            # 准备请求参数
            kwargs = {"headers": headers or {}}
            
            if data is not None:
                if isinstance(data, (dict, list)):
                    kwargs["json"] = data
                    kwargs["headers"]["Content-Type"] = "application/json"
                else:
                    kwargs["data"] = data
            
            async with self.session.request(method, url, **kwargs) as response:
                response_time = time.time() - start_time
                
                try:
                    response_data = await response.json()
                except:
                    response_data = {"text": await response.text()}
                
                # 添加响应头信息
                response_data["headers"] = dict(response.headers)
                
                result = TestResult(
                    test_name=test_name,
                    success=response.status < 400,
                    response_time=response_time,
                    status_code=response.status,
                    response_data=response_data
                )
                
                logger.debug(
                    f"Request completed: {test_name}",
                    method=method,
                    path=path,
                    status_code=response.status,
                    response_time=response_time,
                    success=result.success
                )
                
                self.results.append(result)
                return result
                
        except Exception as e:
            response_time = time.time() - start_time
            result = TestResult(
                test_name=test_name,
                success=False,
                response_time=response_time,
                error_message=str(e)
            )
            
            logger.error(
                f"Request failed: {test_name}",
                method=method,
                path=path,
                error=str(e),
                response_time=response_time
            )
            
            self.results.append(result)
            return result
    
    async def _generate_test_report(self):
        """生成测试报告"""
        total_tests = len(self.results)
        successful_tests = sum(1 for r in self.results if r.success)
        failed_tests = total_tests - successful_tests
        
        avg_response_time = sum(r.response_time for r in self.results) / total_tests if total_tests > 0 else 0
        
        # 按状态码分组
        status_codes = {}
        for result in self.results:
            if result.status_code:
                status_codes[result.status_code] = status_codes.get(result.status_code, 0) + 1
        
        # 生成报告
        report = {
            "summary": {
                "total_tests": total_tests,
                "successful_tests": successful_tests,
                "failed_tests": failed_tests,
                "success_rate": successful_tests / total_tests if total_tests > 0 else 0,
                "average_response_time": avg_response_time
            },
            "status_codes": status_codes,
            "failed_tests": [
                {
                    "test_name": r.test_name,
                    "error_message": r.error_message,
                    "status_code": r.status_code
                }
                for r in self.results if not r.success
            ]
        }
        
        # 保存报告到文件
        report_path = Path(__file__).parent / "test_report.json"
        with open(report_path, 'w') as f:
            json.dump(report, f, indent=2)
        
        # 打印摘要
        logger.info("📊 Test Report Summary")
        logger.info(f"   Total Tests: {total_tests}")
        logger.info(f"   Successful: {successful_tests}")
        logger.info(f"   Failed: {failed_tests}")
        logger.info(f"   Success Rate: {report['summary']['success_rate']:.2%}")
        logger.info(f"   Avg Response Time: {avg_response_time:.3f}s")
        logger.info(f"   Report saved to: {report_path}")
        
        if failed_tests > 0:
            logger.warning(f"❌ {failed_tests} tests failed. Check the report for details.")
        else:
            logger.info("🎉 All tests passed!")


async def main():
    """主函数"""
    import argparse
    
    parser = argparse.ArgumentParser(description="RedFire Gateway Integration Tests")
    parser.add_argument(
        "--gateway-url",
        default="http://localhost:8000",
        help="Gateway URL (default: http://localhost:8000)"
    )
    
    args = parser.parse_args()
    
    async with GatewayIntegrationTester(args.gateway_url) as tester:
        await tester.run_all_tests()


if __name__ == "__main__":
    asyncio.run(main())
